﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Xml.Linq;
using GameEngine.Model.Data;
using PureMVC.Patterns;
using GameEngine.Libs;

namespace GameEngine.Model
{
    /// <summary>
    /// </summary>
    public class ThingTypeDataProxy<IdType> : DataProxy, IThingTypeDataProxy<IdType>
        where IdType : IComparable<IdType>
    {
        public string Name {
            get { return ProxyName; }
        }

        public IThingTypeVO<IdType> VO
        {
            get { return this.Data as IThingTypeVO<IdType>; }
        }
        public IThingTypeVOId Id { get { return VO.Id; } }
        public List<IViewStateVO> ViewStates
        {
            get { return VO.ViewStates; }
            set { VO.ViewStates = value; }
        }
        public IViewDataProxy<IdType> View
        {
            get;
            protected set;
        }

        public ThingTypeDataProxy(
            IThingTypeVO<IdType> typeVO,
            IViewDataProxy<IdType> view)
            : base(IThingTypeDataProxyConstants<IdType>.CreateDataProxyName(typeVO), typeVO)
        {
            View = view;
        }

        //public virtual void LoadFromXML(XDocument doc)
        //{
        //    /*TODO List<List<ITileVO>> tiles = new List<List<ITileVO>>();
        //    foreach (XElement row in doc.Elements("tiles").Nodes())
        //    {
        //        List<ITileVO> tileRow = new List<ITileVO>();
        //        foreach (XElement tile in row.Nodes())
        //        {
        //            ITileVO tileVO = new MyTileVO();
        //            tileVO.Height = float.Parse(tile.Attribute("height").Value);
        //            foreach (XElement node in tile.Nodes())
        //                switch (node.Name.ToString())
        //                {
        //                    case "position":
        //                        tileVO.Position = new Point(
        //                            int.Parse(node.Attribute("x").Value),
        //                            int.Parse(node.Attribute("y").Value));
        //                        break;
        //                }
        //            tileRow.Add(tileVO);
        //        }
        //        tiles.Add(tileRow);
        //    }
        //    Map.Tiles = tiles;
        //    SendNotification(TILESUPDATED_NOTIFICATION, this);*/
        //}

        //public virtual XDocument SaveToXML()
        //{
        //    return null;
        //    /*TODO
        //    XDocument doc = new XDocument();
        //    XElement mapNode = new XElement("map");
        //    XElement maxHeight = new XElement("maxHeight") { Value = Map.MaxHeight.ToString() };
        //    mapNode.Add(maxHeight);
        //    XElement size = new XElement("size");
        //    size.SetAttributeValue("x", Map.Size.X);
        //    size.SetAttributeValue("y", Map.Size.Y);
        //    mapNode.Add(size);
        //    XElement tileSize = new XElement("tileSize");
        //    tileSize.SetAttributeValue("x", Map.TileSize.X);
        //    tileSize.SetAttributeValue("y", Map.TileSize.Y);
        //    mapNode.Add(tileSize);
        //    //todo Camera
        //    XElement tilesNode = new XElement("tiles");
        //    foreach (List<ITileVO> tiles in Map.Tiles)
        //    {
        //        XElement rowNode = new XElement("row");
        //        foreach (ITileVO tile in tiles)
        //        {
        //            XElement tileNode = new XElement("tile");
        //            tileNode.SetAttributeValue("height", tile.Height.ToString());
        //            XElement position = new XElement("position");
        //            position.SetAttributeValue("x", tile.Position.X);
        //            position.SetAttributeValue("y", tile.Position.Y);
        //            tileNode.Add(position);
        //            rowNode.Add(tileNode);
        //        }
        //        tilesNode.Add(rowNode);
        //    }
        //    mapNode.Add(tilesNode);
        //    doc.Add(mapNode);
        //    return doc;*/
        //}

        //public override string Serialize()
        //{
        //    return Serializer.Serialize(this);
        //}
    }

    public class SensingThingTypeDataProxy<ItemVOId> : ThingTypeDataProxy<ItemVOId>, ISensingThingTypeDataProxy<ItemVOId>
        where ItemVOId : IComparable<ItemVOId>
    {
        new protected ISensingThingTypeDataProxy<ItemVOId> VO
        {
            get { return this.Data as ISensingThingTypeDataProxy<ItemVOId>; }
        }
        public IDictionary<string, int> SensibilityReach { get; protected set; }
        public IDictionary<string, SensibilityRadiusEnum> SensibilityRadius { get; protected set; }
        /// <summary>
        /// To be setted when reach, radius and senses are changed
        /// MapPositions relativas ao próprio, sem ter em conta a direccao
        /// </summary>
        public IDictionary<string, List<Point>> InReachMapPositionsRelative { get; set; }
        public List<string> Senses { get; protected set; }

        public SensingThingTypeDataProxy(
            ISensingThingTypeVO<ItemVOId> typeVO,
            IViewDataProxy<ItemVOId> view)
            : base(typeVO, view)
        {
            SensibilityReach = new Dictionary<string, int>();
            SensibilityRadius = new Dictionary<string, SensibilityRadiusEnum>();
            InReachMapPositionsRelative = new Dictionary<string, List<Point>>();
            Senses = new List<string>();
        }

        public void AddSense(string name, int reach, SensibilityRadiusEnum radius)
        {
            Senses.Add(name);
            SensibilityReach.Add(name, reach);
            SensibilityRadius.Add(name, radius);
            InReachMapPositionsRelative.Add(name,new List<Point>());
            if (radius == SensibilityRadiusEnum.forward || radius == SensibilityRadiusEnum.forwardAndDiagonal
                || radius == SensibilityRadiusEnum.forwardAndSides || radius == SensibilityRadiusEnum.forwardAndBackDiagonal
                || radius == SensibilityRadiusEnum.forwardAndBack)
            {
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(0, i));
                }
            }
            if (radius == SensibilityRadiusEnum.forwardAndDiagonal
              || radius == SensibilityRadiusEnum.forwardAndSides || radius == SensibilityRadiusEnum.forwardAndBackDiagonal
              || radius == SensibilityRadiusEnum.forwardAndBack)
            {
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(i, i));
                }
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(-i, i));
                }
            }
            if (radius == SensibilityRadiusEnum.forwardAndSides || radius == SensibilityRadiusEnum.forwardAndBackDiagonal
            || radius == SensibilityRadiusEnum.forwardAndBack)
            {
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(i, 0));
                }
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(-i, 0));
                }
            }
            if (radius == SensibilityRadiusEnum.forwardAndBackDiagonal
           || radius == SensibilityRadiusEnum.forwardAndBack)
            {
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(i, -i));
                }
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(-i, -i));
                }
            }
            if (radius == SensibilityRadiusEnum.forwardAndBack)
            {
                for (int i = 1; i <= reach; i++)
                {
                    InReachMapPositionsRelative[name].Add(new Point(0, -i));
                }
            }
            //TODO para i>1 falta fazer os espaços intermédios
        }

        //public override void LoadFromXML(XDocument doc)
        //{
        //    /*TODO List<List<ITileVO>> tiles = new List<List<ITileVO>>();
        //    foreach (XElement row in doc.Elements("tiles").Nodes())
        //    {
        //        List<ITileVO> tileRow = new List<ITileVO>();
        //        foreach (XElement tile in row.Nodes())
        //        {
        //            ITileVO tileVO = new MyTileVO();
        //            tileVO.Height = float.Parse(tile.Attribute("height").Value);
        //            foreach (XElement node in tile.Nodes())
        //                switch (node.Name.ToString())
        //                {
        //                    case "position":
        //                        tileVO.Position = new Point(
        //                            int.Parse(node.Attribute("x").Value),
        //                            int.Parse(node.Attribute("y").Value));
        //                        break;
        //                }
        //            tileRow.Add(tileVO);
        //        }
        //        tiles.Add(tileRow);
        //    }
        //    Map.Tiles = tiles;
        //    SendNotification(TILESUPDATED_NOTIFICATION, this);*/
        //}

        //public override XDocument SaveToXML()
        //{
        //    return null;
        //    /*TODO
        //    XDocument doc = new XDocument();
        //    XElement mapNode = new XElement("map");
        //    XElement maxHeight = new XElement("maxHeight") { Value = Map.MaxHeight.ToString() };
        //    mapNode.Add(maxHeight);
        //    XElement size = new XElement("size");
        //    size.SetAttributeValue("x", Map.Size.X);
        //    size.SetAttributeValue("y", Map.Size.Y);
        //    mapNode.Add(size);
        //    XElement tileSize = new XElement("tileSize");
        //    tileSize.SetAttributeValue("x", Map.TileSize.X);
        //    tileSize.SetAttributeValue("y", Map.TileSize.Y);
        //    mapNode.Add(tileSize);
        //    //todo Camera
        //    XElement tilesNode = new XElement("tiles");
        //    foreach (List<ITileVO> tiles in Map.Tiles)
        //    {
        //        XElement rowNode = new XElement("row");
        //        foreach (ITileVO tile in tiles)
        //        {
        //            XElement tileNode = new XElement("tile");
        //            tileNode.SetAttributeValue("height", tile.Height.ToString());
        //            XElement position = new XElement("position");
        //            position.SetAttributeValue("x", tile.Position.X);
        //            position.SetAttributeValue("y", tile.Position.Y);
        //            tileNode.Add(position);
        //            rowNode.Add(tileNode);
        //        }
        //        tilesNode.Add(rowNode);
        //    }
        //    mapNode.Add(tilesNode);
        //    doc.Add(mapNode);
        //    return doc;*/
        //}

    }

    public class MovingThingTypeDataProxy<ItemVOId> : SensingThingTypeDataProxy<ItemVOId>, IMovingThingTypeDataProxy<ItemVOId>
        where ItemVOId : IComparable<ItemVOId>
    {
        new protected IMovingThingTypeVO<ItemVOId> VO
        {
            get { return this.Data as IMovingThingTypeVO<ItemVOId>; }
        }

        public float Velocity {
            get { return VO.Velocity; }
            set { VO.Velocity = value; } 
        }

        public MovingThingTypeDataProxy(
            IMovingThingTypeVO<ItemVOId> typeVO,
            IViewDataProxy<ItemVOId> view)
            : base(typeVO, view)
        {
        }

        //public override void LoadFromXML(XDocument doc)
        //{
        //    /*TODO List<List<ITileVO>> tiles = new List<List<ITileVO>>();
        //    foreach (XElement row in doc.Elements("tiles").Nodes())
        //    {
        //        List<ITileVO> tileRow = new List<ITileVO>();
        //        foreach (XElement tile in row.Nodes())
        //        {
        //            ITileVO tileVO = new MyTileVO();
        //            tileVO.Height = float.Parse(tile.Attribute("height").Value);
        //            foreach (XElement node in tile.Nodes())
        //                switch (node.Name.ToString())
        //                {
        //                    case "position":
        //                        tileVO.Position = new Point(
        //                            int.Parse(node.Attribute("x").Value),
        //                            int.Parse(node.Attribute("y").Value));
        //                        break;
        //                }
        //            tileRow.Add(tileVO);
        //        }
        //        tiles.Add(tileRow);
        //    }
        //    Map.Tiles = tiles;
        //    SendNotification(TILESUPDATED_NOTIFICATION, this);*/
        //}

        //public override XDocument SaveToXML()
        //{
        //    return null;
        //    /*TODO
        //    XDocument doc = new XDocument();
        //    XElement mapNode = new XElement("map");
        //    XElement maxHeight = new XElement("maxHeight") { Value = Map.MaxHeight.ToString() };
        //    mapNode.Add(maxHeight);
        //    XElement size = new XElement("size");
        //    size.SetAttributeValue("x", Map.Size.X);
        //    size.SetAttributeValue("y", Map.Size.Y);
        //    mapNode.Add(size);
        //    XElement tileSize = new XElement("tileSize");
        //    tileSize.SetAttributeValue("x", Map.TileSize.X);
        //    tileSize.SetAttributeValue("y", Map.TileSize.Y);
        //    mapNode.Add(tileSize);
        //    //todo Camera
        //    XElement tilesNode = new XElement("tiles");
        //    foreach (List<ITileVO> tiles in Map.Tiles)
        //    {
        //        XElement rowNode = new XElement("row");
        //        foreach (ITileVO tile in tiles)
        //        {
        //            XElement tileNode = new XElement("tile");
        //            tileNode.SetAttributeValue("height", tile.Height.ToString());
        //            XElement position = new XElement("position");
        //            position.SetAttributeValue("x", tile.Position.X);
        //            position.SetAttributeValue("y", tile.Position.Y);
        //            tileNode.Add(position);
        //            rowNode.Add(tileNode);
        //        }
        //        tilesNode.Add(rowNode);
        //    }
        //    mapNode.Add(tilesNode);
        //    doc.Add(mapNode);
        //    return doc;*/
        //}

    }

    public class ThinkingThingTypeDataProxy<ItemVOId> : MovingThingTypeDataProxy<ItemVOId>, IThinkingThingTypeDataProxy<ItemVOId>
        where ItemVOId : IComparable<ItemVOId>
    {
        new protected IThinkingThingTypeVO<ItemVOId> VO
        {
            get { return this.Data as IThinkingThingTypeVO<ItemVOId>; }
        }

        public ThinkingThingTypeDataProxy(
            IThinkingThingTypeVO<ItemVOId> typeVO,
            IViewDataProxy<ItemVOId> view)
            : base(typeVO, view)
        {
            //Brain = new NeuralNetwork(1, new int[] { 10, 10, 0 });

            ////Learn basic type actions
            //new GeneticLearningAlgorithm(Brain).Learn(
            //    new float[][]{
            //        new float[]{0}
            //    },
            //    new float[][]{
            //        new float[]{}
            //    });
        }

        //public override void LoadFromXML(XDocument doc)
        //{
        //    /*TODO List<List<ITileVO>> tiles = new List<List<ITileVO>>();
        //    foreach (XElement row in doc.Elements("tiles").Nodes())
        //    {
        //        List<ITileVO> tileRow = new List<ITileVO>();
        //        foreach (XElement tile in row.Nodes())
        //        {
        //            ITileVO tileVO = new MyTileVO();
        //            tileVO.Height = float.Parse(tile.Attribute("height").Value);
        //            foreach (XElement node in tile.Nodes())
        //                switch (node.Name.ToString())
        //                {
        //                    case "position":
        //                        tileVO.Position = new Point(
        //                            int.Parse(node.Attribute("x").Value),
        //                            int.Parse(node.Attribute("y").Value));
        //                        break;
        //                }
        //            tileRow.Add(tileVO);
        //        }
        //        tiles.Add(tileRow);
        //    }
        //    Map.Tiles = tiles;
        //    SendNotification(TILESUPDATED_NOTIFICATION, this);*/
        //}

        //public override XDocument SaveToXML()
        //{
        //    return null;
        //    /*TODO
        //    XDocument doc = new XDocument();
        //    XElement mapNode = new XElement("map");
        //    XElement maxHeight = new XElement("maxHeight") { Value = Map.MaxHeight.ToString() };
        //    mapNode.Add(maxHeight);
        //    XElement size = new XElement("size");
        //    size.SetAttributeValue("x", Map.Size.X);
        //    size.SetAttributeValue("y", Map.Size.Y);
        //    mapNode.Add(size);
        //    XElement tileSize = new XElement("tileSize");
        //    tileSize.SetAttributeValue("x", Map.TileSize.X);
        //    tileSize.SetAttributeValue("y", Map.TileSize.Y);
        //    mapNode.Add(tileSize);
        //    //todo Camera
        //    XElement tilesNode = new XElement("tiles");
        //    foreach (List<ITileVO> tiles in Map.Tiles)
        //    {
        //        XElement rowNode = new XElement("row");
        //        foreach (ITileVO tile in tiles)
        //        {
        //            XElement tileNode = new XElement("tile");
        //            tileNode.SetAttributeValue("height", tile.Height.ToString());
        //            XElement position = new XElement("position");
        //            position.SetAttributeValue("x", tile.Position.X);
        //            position.SetAttributeValue("y", tile.Position.Y);
        //            tileNode.Add(position);
        //            rowNode.Add(tileNode);
        //        }
        //        tilesNode.Add(rowNode);
        //    }
        //    mapNode.Add(tilesNode);
        //    doc.Add(mapNode);
        //    return doc;*/
        //}
        public virtual float[] BrainInput(IWorldDataProxy<ItemVOId> world, IThingDataProxy<ItemVOId> item)
        {
            List<float> input = new List<float>();
            input.Add(0);
            return input.ToArray();
        }
        public virtual void BrainOutput(IThingDataProxy<ItemVOId> item, float[] output)
        {
            //Parse output and transform into actions
        }
    }
}